﻿/*
 *  This file is part of SXAPI.
 *
 *  SXAPI is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, version 3 of the License.

 *  SXAPI is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with SXAPI.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Reflection;

namespace StackExchangeApi
{
    /// <summary>
    /// Common base class for Answers and Questions.
    /// </summary>
    public abstract class Post : ICached
    {
        private Object m_set_post_sync = new Object();

        [BacksCache]
        private volatile bool m_votes_set;
        private long m_votes;

        [BacksCache]
        private volatile String m_title;
        [BacksCache]
        private volatile String m_summary;
        [BacksCache]
        private volatile String m_author;

        /// <summary>
        /// Aggregate value of the votes this post has received (UPVOTES - DOWNVOTES)
        /// </summary>
        public long Votes
        {
            get
            {
                if (!m_votes_set)
                {
                    lock (m_set_post_sync)
                    {
                        if(!m_votes_set)
                            SetPostValues(this);
                    }
                }

                return m_votes;
            }

            internal set
            {
                m_votes = value;
                m_votes_set = true;
            }
        }

        /// <summary>
        /// Title of this post
        /// </summary>
        public String Title
        {
            get
            {
                if (m_title == null)
                {
                    lock (m_set_post_sync)
                    {
                        if (m_title == null)
                            SetPostValues(this);
                    }
                }

                return m_title;
            }
            
            internal set
            {
                m_title = value;
            }
        }

        /// <summary>
        /// ID of this post
        /// </summary>
        public long Id { get; internal set; }

        /// <summary>
        /// Site this post belongs to
        /// </summary>
        public StackExchangeSite Site {get; internal set;}

        /// <summary>
        /// A summary of this Post (HTML encoded)
        /// </summary>
        public String Summary
        {
            get
            {
                if (m_summary == null)
                {
                    lock (m_set_post_sync)
                    {
                        if (m_summary == null)
                            SetPostValues(this);
                    }
                }

                return m_summary;
            }

            internal set { m_summary = value; }
        }

        /// <summary>
        /// The name of the author
        /// </summary>
        public String Author
        {
            get
            {
                if (m_author == null)
                {
                    lock (m_set_post_sync)
                    {
                        if (m_author == null)
                        {
                            SetPostValues(this);
                        }
                    }
                }

                return m_author;
            }

            internal set { m_author = value; }
        }

        /// <summary>
        /// Construct a new post.
        /// 
        /// Note that construction will succeed even if the post does not exist.
        /// </summary>
        /// <param name="id">ID of the post</param>
        /// <param name="site">Site this post is on</param>
        internal Post(long id, StackExchangeSite site)
        {
            Id = id;
            Site = site;
        }

        /// <summary>
        /// Construct a new post.
        /// 
        /// Note that construction will succeed even if the post does not exist.
        /// </summary>
        /// <param name="votes">Aggregate votes this post has received</param>
        /// <param name="id">Id of the post</param>
        /// <param name="site">Site this post is on</param>
        internal Post(long votes, long id, StackExchangeSite site)
            : this(id, site)
        {
            Votes = votes;
        }

        /// <summary>
        /// Construct a new post.
        /// 
        /// Note that the construction will succeed even if the post does not exist.
        /// </summary>
        /// <param name="title">Title of the post</param>
        /// <param name="votes">Aggregate votes this post has receieved</param>
        /// <param name="id">Id of the post</param>
        /// <param name="site">Site this post is on</param>
        internal Post(String title, long votes, long id, StackExchangeSite site)
            : this(votes, id, site)
        {
            Title = title;
        }

        /// <summary>
        /// Called to indicate that this post needs its fields populated.
        /// 
        /// Should be overridden in descendant classes to populate ALL fields.
        /// This implemenation does nothing.
        /// </summary>
        /// <param name="other">Post to populate</param>
        internal virtual void SetPostValues(Post other){ }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public virtual void Clear()
        {
            Utils.ICachedImpl(this);
        }
    }
}
